    class MoonCake {
            constructor(options) {
                this.x = 0;
                this.y = 0;
                this.name = "五仁";
                this.strokeStyle = "rgb(180,110,48)";
                this.fillStyle = "rgb(250,201,81)";
                this.fontSize = 36;
                this.scale = 1;
                Object.assign(this, options);
                this.ctx = null;
                this.progress = -1;
                this.stepFn = [];
                this.isComplete = false;
                this.nowDate = new Date();
                this.lastDate = new Date();
                return this;
            }
            render(ctx) {
                if (!ctx)
                throw new Error("context is undefined.");
                this.ctx = ctx;
                this.stepFn.length = 0;
                this.stepFn.push(() => this.drawEdge(180, 20));
                this.stepFn.push(() => this.drawEdge(140, 12));
                this.stepFn.push(() => this.drawRoundRectPath(140, 220, 40));
                this.stepFn.push(() => this.drawRoundRectPath(220, 140, 40));
                this.stepFn.push(() => this.drawLine(30, -110, 30, 110));
                this.stepFn.push(() => this.drawLine(0, -110, 0, 110));
                this.stepFn.push(() => this.drawLine(-30, -110, -30, 110));
                this.stepFn.push(() => this.drawLine(-110, -30, 110, -30));
                this.stepFn.push(() => this.drawLine(-110, 0, 110, 0));
                this.stepFn.push(() => this.drawLine(-110, 30, 110, 30));
                this.stepFn.push(() => this.drawRect(140, 140));
                this.stepFn.push(() => this.drawBox(140));
                this.stepFn.push(() => this.drawText());
                return this;
            }
            draw() {
                for (let i = 0; i < this.progress; i++) {
                this.stepFn[i] && this.stepFn[i]();
                }
                if (this.progress > this.stepFn.length) return this.isComplete = true;
                this.nowDate = new Date();
            
                if (this.nowDate - this.lastDate > 200) {
                this.progress++;
                this.lastDate = this.nowDate;
                }
            }
            drawText(n) {
                const { ctx, x, y, name, fontSize, strokeStyle, scale } = this;
                let size = fontSize;
                ctx.save();
                ctx.translate(x, y);
                ctx.scale(scale, scale);
                ctx.fillStyle = strokeStyle;
                ctx.textAlign = "center";
                ctx.font = `bolder ${size}px fangsong,self`;
                ctx.shadowColor = strokeStyle;
                ctx.shadowBlur = 2;
                ctx.shadowOffsetX = -1;
                ctx.shadowOffsety = -1;
                if (name.length == 2) {
                ctx.fillText(name.charAt(0), 0, -size * 0.5 + 5);
                ctx.fillText(name.charAt(1), 0, size * 0.5 + 5);
                }
                if (name.length >= 3) {
                size *= 0.7;
                ctx.font = `bolder ${size}px fangsong,self`;
                ctx.fillText(name.charAt(0), 0, -size * 1 + 2);
                ctx.fillText(name.charAt(1), 0, size * 0 + 2);
                ctx.fillText(name.charAt(2), 0, size * 1 + 2);
                }
                ctx.restore();
            }
            drawBox(size) {
                const { ctx, x, y, strokeStyle, scale } = this;
                let v = 17,
                n = -size / 2;
                ctx.save();
                ctx.translate(x, y);
                ctx.scale(scale, scale);
                ctx.beginPath();
                ctx.lineCap = "round";
                ctx.lineWidth = 4;
                ctx.strokeStyle = strokeStyle;
                ctx.moveTo(v + n, n);
                ctx.lineTo(v + n, size - v + n);
                ctx.lineTo(size - v + n, size - v + n);
                ctx.lineTo(size - v + n, v + n);
                ctx.lineTo(v * 2 + n, v + n);
                ctx.lineTo(v * 2 + n, size - v * 2 + n);
                ctx.lineTo(size - v * 2 + n, size - v * 2 + n);
                ctx.lineTo(size - v * 2 + n, 45 + n);
                ctx.stroke();
                ctx.restore();
            
            }
            drawLine(x1, y1, x2, y2) {
                const { ctx, x, y, strokeStyle, scale } = this;
                ctx.save();
                ctx.translate(x, y);
                ctx.scale(scale, scale);
                ctx.beginPath();
                ctx.lineCap = "round";
                ctx.lineWidth = 4;
                ctx.strokeStyle = strokeStyle;
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.stroke();
                ctx.restore();
            }
            drawRect(width, height) {
                const { ctx, x, y, strokeStyle, fillStyle, scale } = this;
                ctx.save();
                ctx.translate(x, y);
                ctx.scale(scale, scale);
                ctx.beginPath();
                ctx.lineCap = "round";
                ctx.lineWidth = 4;
                ctx.strokeStyle = strokeStyle;
                ctx.fillStyle = fillStyle;
                ctx.rect(-width / 2, -height / 2, width, width);
                ctx.fill();
                ctx.stroke();
                ctx.restore();
            }
            drawRoundRectPath(width, height, radius) {
                const { ctx, x, y, strokeStyle, fillStyle, scale } = this;
                let w = -width / 2,
                h = -height / 2;
                ctx.save();
                ctx.translate(x, y);
                ctx.scale(scale, scale);
                ctx.lineCap = "round";
                ctx.strokeStyle = strokeStyle;
                ctx.fillStyle = fillStyle;
                ctx.lineWidth = 5;
                ctx.beginPath();
                ctx.arc(width - radius + w, height - radius + h, radius, 0, Math.PI / 2);
                ctx.lineTo(radius + w, height + h);
                ctx.arc(radius + w, height - radius + h, radius, Math.PI / 2, Math.PI);
                ctx.lineTo(w, radius + h);
                ctx.arc(radius + w, radius + h, radius, Math.PI, Math.PI * 3 / 2);
                ctx.lineTo(width - radius + w, h);
                ctx.arc(width - radius + w, radius + h, radius, Math.PI * 3 / 2, Math.PI * 2);
                ctx.lineTo(width + w, height - radius + h);
                ctx.closePath();
                ctx.stroke();
                ctx.restore();
            }
            drawEdge(radius, lineWidth) {
                const { ctx, x, y, strokeStyle, fillStyle, scale } = this;
                let n = 12,
                v = 360 / n,
                m = 30;
                ctx.save();
                ctx.translate(x, y);
                ctx.scale(scale, scale);
                ctx.beginPath();
                ctx.lineCap = "round";
                for (let i = 0; i < n; i++) {
                let angle1 = i * v * Math.PI / 180;
                let angle2 = (i + 1) * v * Math.PI / 180;
                let angle3 = (i + 0.5) * v * Math.PI / 180;
                ctx.lineWidth = lineWidth;
                ctx.strokeStyle = strokeStyle;
                ctx.fillStyle = fillStyle;
                let _sx = radius * Math.cos(angle1),
                _sy = radius * Math.sin(angle1);
                ctx.lineTo(_sx, _sy);
                let _ex = radius * Math.cos(angle2),
                _ey = radius * Math.sin(angle2);
            
                let _mx = (radius + m) * Math.cos(angle3),
                _my = (radius + m) * Math.sin(angle3);
                ctx.bezierCurveTo(_mx, _my, _ex, _ey, _ex, _ey);
                }
                ctx.closePath();
                ctx.stroke();
                ctx.fill();
                ctx.restore();
        }}
            
            
    class Application {
        constructor() {
            this.canvas = null;
            this.ctx = null;
            this.w = 0;
            this.h = 0;
            this.types = [
            "五仁", "蛋黄", "莲蓉", "豆沙", "芝麻",
            "草莓", "火腿", "巧克力", "咸肉", "椰蓉",
            "榴莲", "小龙虾", "哈蜜瓜", "螺狮", "番薯",
            "果仁", "蟹黄", "蓝莓", "抹茶", "芒果",
            "福气", "财气", "好运"];
        
            this.words = [
            "但愿人长久,千里共婵娟",
            "花好月圆佳节夜,思念千里梦甜甜",
            "中秋快乐身体棒,心想事成您准赢",
            "凉意好清风,万事皆得兴",
            "如果快乐是本领的话,我希望你是最厉害的",
            "愿笑口常开,好彩自然来",
            "荷叶圆圆水中立,情谊缘缘永不变",
            "千江月明千江水,万里花飘万里情",
            "月明圆峤人千里,风急轻帆燕一行",
            "我予汝心天可表,千里也能共婵娟",
            "皓月当空洒清辉,中秋良宵念挚心",
            "月中有株婆娑树,婆娑树上挂紫微"];
        
            this.moonCake = null;
            this.text = "";
            this.textIndex = 0;
            this.dt = 0;
            this.init();
        }
        init() {
            this.canvas = document.getElementById("canvas");
            this.ctx = this.canvas.getContext("2d");
            window.addEventListener("resize", this.reset.bind(this));
            this.canvas.addEventListener("click", () => {
            if (this.moonCake && this.moonCake.isComplete) {
                this.reset();
            }
            });
            this.reset();
            this.render();
            this.step();
        }
        reset() {
            this.dt = 0;
            this.w = this.canvas.width = this.ctx.width = window.innerWidth;
            this.h = this.canvas.height = this.ctx.height = window.innerHeight;
            this.render();
        }
        render() {
            const { w, h, ctx, types, words } = this;
            this.text = words[~~(words.length * Math.random())].split(",");
            this.textIndex = 0;
            this.moonCake = new MoonCake({
            x: w / 2,
            y: h / 2,
            scale: .85,
            name: types[~~(types.length * Math.random())] }).
            render(ctx);
        }
        drawText() {
            const { ctx, w, h, text } = this;
            ctx.save();
            ctx.fillStyle = "rgb(253,190,0)";
            ctx.textAlign = "center";
            ctx.font = `bolder 32px fangsong,self`;
            ctx.shadowColor = "rgb(253,190,0)";
            ctx.shadowBlur = 10;
            ctx.fillText(text[0].substr(0, this.textIndex), w / 2, h * 0.36 + 240);
            if (text[0].length < this.textIndex) {
            ctx.fillText(text[1].substr(0, this.textIndex - text[0].length), w / 2, h * 0.36 + 240 + 52);
            }
            ctx.restore();
        }
        step() {
            requestAnimationFrame(this.step.bind(this));
            const { ctx, w, h } = this;
            ctx.clearRect(0, 0, w, h);
            this.moonCake && this.moonCake.draw();
            if (this.moonCake.isComplete) {
            this.moonCake.y -= 2;
            this.moonCake.y = Math.max(h * 0.36, this.moonCake.y);
            if (this.moonCake.y == h * 0.36) {
                this.drawText();
                if (this.dt % 10 == 0 && this.textIndex < this.text.join("").length) {
                this.textIndex++;
                }
            }
            }
            this.dt++;
        
        }}
                
 
        window.onload = new Application();